/*
    工具模块：其他模块共同使用的工具
    字符串、文件、进度条等相关工具
*/

#pragma once
#include <boost/algorithm/string.hpp>
#include <fstream>
#include <mutex>
#include <unordered_set>
#include <cstdio>
#include <unistd.h>
#include "log.hpp"
#include "include/cppjieba/Jieba.hpp"



namespace ns_util
{
    class FileUtil
    {
    public:
        static bool readFile(const std::string& filepath, std::string* pstr) //将filepath文件的内容保存到pstr指向空间
        {
            std::ifstream in(filepath);
            if(!in.is_open())
            {
                LOG(ERROR, "读文件%s失败\n", filepath.c_str());
                return false;
            }

            std::string line;
            while(getline(in, line))
            {
                *pstr += line;
            }

            in.close();
            return true;
        }
    };

    class StrUtil
    {
    public:
        static void splitStr(const std::string& s, std::vector<std::string>* tokens, std::string sep)
        {
            boost::split(*tokens, s, boost::is_any_of(sep), boost::token_compress_on);
        }
    };

    // jieba分词所依赖的词库
    const char* const DICT_PATH = "./dict/jieba.dict.utf8";
    const char* const HMM_PATH = "./dict/hmm_model.utf8";
    const char* const USER_DICT_PATH = "./dict/user.dict.utf8";
    const char* const IDF_PATH = "./dict/idf.utf8";
    const char* const STOP_WORD_PATH = "./dict/stop_words.utf8";
    class JiebaUtil //设计成单例
    {
    private:
        JiebaUtil():_jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH)
        {
        }
        JiebaUtil(const JiebaUtil&) = delete;
        JiebaUtil& operator=(const JiebaUtil&) = delete;
    private:
        void initJiebaUtil(const char* const filepath)
        {
            std::ifstream in(filepath);
            if(!in.is_open())
            {
                LOG(WARRING, "jieba暂停词读取失败\n");
                return;
            }

            std::string word;
            while(getline(in, word))
            {
                _stopwords.insert(word);
            }
            in.close();
        }

        void split(const std::string& s, std::vector<std::string>* tokens)
        {
            // 分词（包含暂停词）
            std::vector<std::string> words;
            _jieba.CutForSearch(s, words);

            // 去除暂停词
            for(auto& word : words)
            {
                if(_stopwords.find(word) == _stopwords.end())
                    tokens->push_back(std::move(word));
            }
        }
    public:
        static JiebaUtil* getInstance()
        {
            if(_jbutil == nullptr)
            {
                _mtx.lock();
                if(_jbutil == nullptr)
                {
                    _jbutil = new JiebaUtil();
                    _jbutil->initJiebaUtil(STOP_WORD_PATH);
                }
                _mtx.unlock();
            }
            return _jbutil;
        }

        // 分词
        static void splitWord(const std::string& s, std::vector<std::string>* tokens)
        {
            ns_util::JiebaUtil::getInstance()->split(s, tokens);
        }
    
    private:
        cppjieba::Jieba _jieba; //jieba对象
        std::unordered_set<std::string> _stopwords; //暂停词
        static JiebaUtil* _jbutil;
        static std::mutex _mtx;
    };
    JiebaUtil* JiebaUtil::_jbutil = nullptr;
    std::mutex JiebaUtil::_mtx;

#define MAX 101
#define BODY '='
#define HEAD '>'
    class ProcessbarUtil
    {
    public:
        static void processbar(double rate)
        {
            static int cnt = 0;
            int n = (int)rate;
            int i = 0;
            for(i = 0; i < n; i++)
            {
                bar[i] = BODY;
            }
            if(i < 100)
                bar[i] = HEAD;
            printf("[%-100s][%.2f%%][%c]\r", bar, rate, label[(cnt++)%4]);
            fflush(stdout);
            if(rate == 100.0)
                printf("\n");
            usleep(100000);
        }
    private:
    static char* bar;
    static const char* const label;
    };
    char* ProcessbarUtil::bar = new char[MAX];
    const char* const ProcessbarUtil::label = "-\\|/";
}
